import json
import random
from pathlib import Path
from dataclasses import dataclass
from typing import NamedTuple, Dict, Literal, Tuple, cast


class PunchProfile(NamedTuple):
    choices: int        # 抉择
    threshold: int      # 阈值
    hit_damage: int     # 命中伤害
    block_damage: int   # 格挡伤害

    pre_msg: str        # 出拳提示
    hit_msg: str        # 命中提示
    blocked_msg: str    # 格挡提示

    knockout_possible: bool = False     # 击倒可能


    def is_hit(self):
        return random.randint(1, self.choices) <= self.threshold


@dataclass
class Player:
    name: str
    best: int  # 特长（你的特长拳法会额外造成2点伤害）
    weakness: int  # 弱点（如果对手的招式恰巧为你的弱点，你总是不能闪避
    # 可以通过is_computer来判断目前是玩家还是电脑
    is_computer: bool
    # 你可以用Literal来限制input
    punch_profiles: Dict[Literal[1, 2, 3, 4], PunchProfile]
    damage: int = 0
    score: int = 0
    knockedout: bool = False

    @property
    def get_punch_choice(self) -> int:
        # 生成电脑的拳法通过随机数
        if self.is_computer:
            return random.randint(1,4)

        # 通过while死循环要求玩家必须选择1-4中任意一个拳法
        # while punch not in [1, 2, 3, 4]:
        #     print(f"{self.name}的招式是？")
        #     # 可以加上是不是数字的判断以免不是数字卡死报错
        #     punch = int(input())
        # return punch
        while True:
            try:
                punch = int(input(f"{self.name}的招式是？"))
                if 1 <= punch <= 4:
                    return punch
                else:
                    print("无效的拳法，请输入1-4之间的数字。")
            except ValueError:
                print("请输入拳法序号。")


#击倒阈值
KNOCKOUT_THRESHOLD = 60
#击晕信息
KNOCKOUT_COLD = "{loser}被{winner}击晕了无法继续进行比赛，胜利者是{winner}"


# 获取选手的弱点信息
def get_vulnerability(player1name):
    # print("他的弱点是?")
    # return int(input())
    while True:
        try:
            weakness = int(input(f"{player1name}的弱点是什么拳法？"))
            if 1 <= weakness <= 4:
                return weakness
            else:
                print("请输正确的拳法序号。")
        except ValueError:
            print("请输入拳法序号。")


def get_excel(player2name):
    while True:
        try:
            punch = int(input(f"{player2name}擅长的拳法是？"))
            if 1 <= punch <= 4:
                return punch
            else:
                print("请输入正确的拳法序号。")
        except ValueError:
            print("请输入拳法序号。")


# 获取电脑的弱点信息
def get_opponent_stats() -> Tuple[int, int]:
    opponent_best = 0
    opponent_weakness = 0
    while opponent_best == opponent_weakness:
        opponent_best = random.randint(1, 4)
        opponent_weakness = random.randint(1, 4)

    return opponent_best, opponent_weakness


# 获取招式的基本信息,这段内容不要改动
def read_punch_profiles(filepath: Path):
    with open(filepath, encoding='utf-8') as f:
        punch_profiles_dict = json.load(f)
    return {
        cast(Literal[1, 2, 3, 4], int(key)): PunchProfile(**value)
        for key, value in punch_profiles_dict.items()
    }


def play_round(round_number: int, player: Player, opponent: Player) -> None:
    if opponent.score >= 2 or player.score >= 2:
        return

    print(f"第{round_number}轮次。\n开始....\n")
    # 三局两胜的规则，如果有一方的分数>=2，则直接结束

    for _action in range(7):
        if is_opponents_turn():
            punch = opponent.get_punch_choice
            active = opponent
            passive = player
            opponents_turn = True
        else:
            punch = player.get_punch_choice
            active = player
            passive = opponent
            opponents_turn = False

        # 不用再区分是玩家攻击还是电脑攻击，只做谁主动谁被动的判断
        punch_profile = active.punch_profiles[punch]

        # 如果选中的招式是擅长的招式则攻击+2
        if punch == active.best:
            active.damage = active.damage + 2

        print(punch_profile.pre_msg.format(opponent=opponent, player=player))

        # 如果对手不擅长这个招数，那么他将总是被击中
        if passive.weakness == punch or punch_profile.is_hit():
            print(punch_profile.hit_msg.format(opponent=opponent, player=player))
            # 判定是否存在直接击倒的可能性
            if punch_profile.knockout_possible and active.damage > KNOCKOUT_THRESHOLD:
                if opponents_turn and opponent.damage > KNOCKOUT_THRESHOLD:
                    player.knockedout = True
                    break
                elif opponents_turn == False and player.damage > KNOCKOUT_THRESHOLD:
                    opponent.knockedout = True
                    break
            # 如果不存在击倒可能性则记录伤害
            if opponents_turn == True:
                opponent.damage = opponent.damage + punch_profile.hit_damage
            else:
                player.damage += punch_profile.hit_damage

        else:
            print(punch_profile.blocked_msg.format(opponent=opponent, player=player))
            # 如果对面闪开了你的攻击，可能会因为你的攻击后摇太大对你造成反击
            if opponents_turn:
                player.damage += punch_profile.block_damage
            else:
                opponent.damage += punch_profile.block_damage
        # 如果有人被击晕，直接结束比赛
        if player.knockedout or opponent.knockedout:
            break


    # 这里的分数用伤害统计，不太合理并且每一局的伤害会累计到下一局，也就是说如果你第一局造成大量伤害，第二局没造成伤害也有可能获胜
    if player.damage > opponent.damage:
        player.score += 1
        print(f"{player.name}获得了{player.score}场胜利")

    # elif player.damage == opponent.damage:
    #     print("1比1平局")

    else:
        opponent.score += 1
        print(f"{opponent.name}获得了{opponent.score}场胜利")


# 生成1个1-10的随机数，如果数字大于5则是对面回合
def is_opponents_turn() -> bool:
    return random.randint(1, 10) > 5


def main() -> None:
    print("拳王争霸赛")
    print("由xxx设计创造")
    print("学号: 202312060308")
    print("这个游戏采取三局两胜的规则")
    print("对手名字为:")
    opponent_name = input()
    print("我的名字为:")
    player_name = input()
    print("不同的出拳策略 1 摆拳 2 平勾拳 3 上钩拳 4 刺拳")
    # print("你擅长的拳法是?")
    player_best = get_excel(player_name)  #玩家擅长
    player_weakness = get_vulnerability(player_name)  # 玩家弱点
    player = Player(
        name=player_name,
        best=player_best,
        weakness=player_weakness,
        is_computer=False,
        punch_profiles=read_punch_profiles(
            Path(__file__).parent / "player-profile.json"
        ),
    )

    opponent_best, opponent_weakness = get_opponent_stats()
    opponent = Player(
        name=opponent_name,
        best=opponent_best,
        weakness=opponent_weakness,
        is_computer=True,
        punch_profiles=read_punch_profiles(
            Path(__file__).parent / "opponent-profile.json"
        ),
    )

    # 做一次判断，看看能不能读取对应文件数据
    # print(player)
    # print(opponent)
    for round_number in (1, 2, 3):
        if player.knockedout or opponent.knockedout:
            break
        play_round(round_number, player, opponent)

    if player.knockedout:
        print(KNOCKOUT_COLD.format(loser=player.name, winner=opponent.name))
    elif opponent.knockedout:
        print(KNOCKOUT_COLD.format(loser=opponent.name, winner=player.name))
    elif opponent.score > player.score:
        print(f"{opponent.name}打败了{player.name}")
    else:
        print(f"{player.name}打败了{opponent.name}")

    print("\n\n比赛结束")
    print("感谢游玩")



if __name__ == "__main__":
    main()
